/***************************************************************
 * Name:      laba2Main.cpp
 * Purpose:   Code for Application Frame
 * Author:    Oleksandr Maksymenko (maksymenko.o.v@nmu.one)
 * Created:   2022-12-02
 * Copyright: Oleksandr Maksymenko ()
 * License:
 **************************************************************/

#include "wx_pch.h"
#include <wx/filename.h>
#include "laba2Main.h"
#include <wx/msgdlg.h>
#include <wx/tokenzr.h>
#include <cstdlib>
#include "DCUtils.h"

#define GRID_VIEW_ID    1
#define PAINT_VIEW_ID   2

//(*InternalHeaders(laba2Frame)
#include <wx/intl.h>
#include <wx/string.h>
//*)

//helper functions
enum wxbuildinfoformat
{
    short_f, long_f
};

wxString wxbuildinfo(wxbuildinfoformat format)
{
    wxString wxbuild(wxVERSION_STRING);

    if (format == long_f )
    {
#if defined(__WXMSW__)
        wxbuild << _T("-Windows");
#elif defined(__UNIX__)
        wxbuild << _T("-Linux");
#endif

#if wxUSE_UNICODE
        wxbuild << _T("-Unicode build");
#else
        wxbuild << _T("-ANSI build");
#endif // wxUSE_UNICODE
    }

    return wxbuild;
}

//(*IdInit(laba2Frame)
const long laba2Frame::idMenuOpen = wxNewId();
const long laba2Frame::idMenuSaveAs = wxNewId();
const long laba2Frame::idMenuClose = wxNewId();
const long laba2Frame::idMenuExport = wxNewId();
const long laba2Frame::idMenuQuit = wxNewId();
const long laba2Frame::idMenuTableData = wxNewId();
const long laba2Frame::idMenuGraphicData = wxNewId();
const long laba2Frame::idMenuGenerate = wxNewId();
const long laba2Frame::idMenuAbout = wxNewId();
const long laba2Frame::ID_STATUSBAR1 = wxNewId();
//*)

BEGIN_EVENT_TABLE(laba2Frame,wxFrame)
    //(*EventTable(laba2Frame)
    //*)
END_EVENT_TABLE()

laba2Frame::laba2Frame(wxWindow* parent,wxWindowID id)
{
    //(*Initialize(laba2Frame)
    wxMenu* Menu1;
    wxMenu* Menu2;
    wxMenuBar* MenuBar1;
    wxMenuItem* MenuItem1;
    wxMenuItem* MenuItem2;

    Create(parent, id, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, _T("id"));
    MenuBar1 = new wxMenuBar();
    Menu1 = new wxMenu();
    MenuItem3 = new wxMenuItem(Menu1, idMenuOpen, _("Open"), wxEmptyString, wxITEM_NORMAL);
    Menu1->Append(MenuItem3);
    MenuItem5 = new wxMenuItem(Menu1, idMenuSaveAs, _("Save As..."), wxEmptyString, wxITEM_NORMAL);
    Menu1->Append(MenuItem5);
    MenuItem6 = new wxMenuItem(Menu1, idMenuClose, _("Close"), wxEmptyString, wxITEM_NORMAL);
    Menu1->Append(MenuItem6);
    Menu1->AppendSeparator();
    MenuItem7 = new wxMenuItem(Menu1, idMenuExport, _("Export"), wxEmptyString, wxITEM_NORMAL);
    Menu1->Append(MenuItem7);
    Menu1->AppendSeparator();
    MenuItem1 = new wxMenuItem(Menu1, idMenuQuit, _("Quit\tAlt-F4"), _("Quit the application"), wxITEM_NORMAL);
    Menu1->Append(MenuItem1);
    MenuBar1->Append(Menu1, _("&File"));
    Menu4 = new wxMenu();
    MenuItem8 = new wxMenuItem(Menu4, idMenuTableData, _("Table Data"), wxEmptyString, wxITEM_NORMAL);
    Menu4->Append(MenuItem8);
    MenuItem9 = new wxMenuItem(Menu4, idMenuGraphicData, _("Graphic Data"), wxEmptyString, wxITEM_NORMAL);
    Menu4->Append(MenuItem9);
    MenuBar1->Append(Menu4, _("View"));
    Menu3 = new wxMenu();
    MenuItem4 = new wxMenuItem(Menu3, idMenuGenerate, _("Generate"), wxEmptyString, wxITEM_NORMAL);
    Menu3->Append(MenuItem4);
    MenuBar1->Append(Menu3, _("Processing"));
    Menu2 = new wxMenu();
    MenuItem2 = new wxMenuItem(Menu2, idMenuAbout, _("About\tF1"), _("Show info about this application"), wxITEM_NORMAL);
    Menu2->Append(MenuItem2);
    MenuBar1->Append(Menu2, _("Help"));
    SetMenuBar(MenuBar1);
    StatusBar1 = new wxStatusBar(this, ID_STATUSBAR1, 0, _T("ID_STATUSBAR1"));
    int __wxStatusBarWidths_1[1] = { -1 };
    int __wxStatusBarStyles_1[1] = { wxSB_NORMAL };
    StatusBar1->SetFieldsCount(1,__wxStatusBarWidths_1);
    StatusBar1->SetStatusStyles(1,__wxStatusBarStyles_1);
    SetStatusBar(StatusBar1);

    Connect(idMenuOpen,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&laba2Frame::OnOpenMenuItem);
    Connect(idMenuSaveAs,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&laba2Frame::OnSaveAsMenuItem);
    Connect(idMenuClose,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&laba2Frame::OnCloseMenuItem);
    Connect(idMenuExport,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&laba2Frame::OnExportMenuItem);
    Connect(idMenuQuit,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&laba2Frame::OnQuit);
    Connect(idMenuTableData,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&laba2Frame::OnTableDataSelected);
    Connect(idMenuGraphicData,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&laba2Frame::OnGraphicDataSelected);
    Connect(idMenuGenerate,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&laba2Frame::OnGenerateMenuItem);
    Connect(idMenuAbout,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&laba2Frame::OnAbout);
    //*)

    SetTitle(_("Maksymenko CPP laba2"));

    tmpFilename = _(wxGetCwd() + wxFileName::GetPathSeparator() + "tmp.dat");

    points = new wxPoints();

    wxTextFile file(tmpFilename);
    if(file.Exists())
    {
        file.Open();
        file.GetFirstLine();

        fileToPoints(&file, points);

        wxRemoveFile(tmpFilename);
        file.Close();
    }

    CreateView(GRID_VIEW_ID);
    currentViewId = GRID_VIEW_ID;
}

laba2Frame::~laba2Frame()
{
    DestroyView(GRID_VIEW_ID);
    DestroyView(PAINT_VIEW_ID);
    delete points;
    //(*Destroy(laba2Frame)
    //*)
}

void laba2Frame::OnQuit(wxCommandEvent& event)
{
    if(points->count() > 0)
    {
        wxTextFile file(tmpFilename);

        if(file.Exists())
        {
            wxRemoveFile(tmpFilename);
        }

        file.Create();
        file.Open();

        file.AddLine("X,Y,Z");
        file.Write();

        pointsToFile(points, &file);
        file.Close();
    }

    Close();
}

void laba2Frame::OnAbout(wxCommandEvent& event)
{
    wxString msg = wxbuildinfo(long_f);
    wxMessageBox(msg, _("This is my second lab"));
}

void laba2Frame::OnOpenMenuItem(wxCommandEvent& event)
{
    wxFileDialog openFileDialog(this, _("Open DAT file"), "", "",
                                "DAT files (*.dat)|*.dat", wxFD_OPEN|wxFD_FILE_MUST_EXIST);
    if (openFileDialog.ShowModal() == wxID_CANCEL)
    {
        return;
    }

    wxString path = openFileDialog.GetPath();

    wxFileInputStream input_stream(path);
    if (!input_stream.IsOk())
    {
        wxLogError("Cannot open file '%s'.", path);
        return;
    }

    wxTextFile tfile;
    tfile.Open(path);

    tfile.GetFirstLine();

    points->clear();

    fileToPoints(&tfile, points);

    tfile.Close();

    DestroyView(currentViewId);
    CreateView(currentViewId);
}

void laba2Frame::fileToPoints(wxTextFile *file, wxPoints *points)
{
    while(!file->Eof())
    {
        wxString line;
        line.Printf( _("%s"), file->GetNextLine());

        wxStringTokenizer tokenizer(line, ",");

        if(!tokenizer.HasMoreTokens()) break;

        XYZ point;

        point.x = std::atof(tokenizer.GetNextToken().mb_str());
        point.y = std::atof(tokenizer.GetNextToken().mb_str());
        point.z = std::atof(tokenizer.GetNextToken().mb_str());

        points->addPoint(point);
    }
}

void laba2Frame::pointsToFile(wxPoints *points, wxTextFile *file)
{
    for(int i = 0; i < points->count(); i++)
    {
        XYZ* point = points->getPoint(i);
        wxString line;
        line.Printf( _("%lf, %lf, %lf"), point->x, point->y, point->z);

        file->AddLine(line);
        file->Write();
    }
}

void laba2Frame::OnGenerateMenuItem(wxCommandEvent& event)
{
    wxGenerateDialog generateDialog(this);
    if(generateDialog.ShowModal() == wxID_OK)
    {
        double minX = generateDialog.getMinX();
        double maxX = generateDialog.getMaxX();
        double minY = generateDialog.getMinY();
        double maxY = generateDialog.getMaxY();
        double minZ = generateDialog.getMinZ();
        double maxZ = generateDialog.getMaxZ();
        double amount = generateDialog.getAmount();

        std::default_random_engine rand;

        std::uniform_real_distribution<double> unifX(minX, maxX);
        std::uniform_real_distribution<double> unifY(minY, maxY);
        std::uniform_real_distribution<double> unifZ(minZ, maxZ);

        for(int i = 0; i < amount; i++)
        {
            XYZ point;

            point.x = unifX(rand);
            point.y = unifY(rand);
            point.z = unifZ(rand);

            points->addPoint(point);
        }

        DestroyView(currentViewId);
        CreateView(currentViewId);
    }
}

void laba2Frame::OnSaveAsMenuItem(wxCommandEvent& event)
{
    wxFileDialog fileDialog(this, _("Specify a name and location for coords"),
            "", "", _("coords (*.dat)|*.dat"), wxFD_SAVE);

    int response = fileDialog.ShowModal();
    if(response == wxID_OK)
    {
        wxString saveFilename = fileDialog.GetPath();
        wxTextFile file(saveFilename);
        wxString logMsg;

        if(file.Exists())
        {
            wxRemoveFile(saveFilename);
            logMsg = _("Overrided file: %s");
        }
        else
        {
            logMsg = _("Created file: %s");
        }
        file.Create();
        file.Open();

        //adding header line
        file.AddLine("X,Y,Z");
        file.Write();

        pointsToFile(points, &file);
        file.Close();

        wxLogMessage(logMsg, saveFilename);
    }
}

void laba2Frame::OnCloseMenuItem(wxCommandEvent& event)
{
    points->clear();

    DestroyView(currentViewId);
    CreateView(currentViewId);
}

void laba2Frame::CreateView(int viewId)
{
    switch(viewId)
    {
    case GRID_VIEW_ID:
        gridView = new wxGridView(this, (const wxPoints *) points);
        break;
    case PAINT_VIEW_ID:
        //TODO
        paintView = new wxPaintView(this, (const wxPoints *) points);
        paintView->SetDrawFlag(true);
        break;
    }

    PostSizeEvent();
}

void laba2Frame::DestroyView(int viewId)
{
    switch(viewId)
    {
    case GRID_VIEW_ID:
        if(gridView != nullptr && gridView->Destroy())
        {
            gridView = nullptr;
        }
        break;
    case PAINT_VIEW_ID:
        if(paintView != nullptr)
        {
            paintView->SetDrawFlag(false);
            if(paintView->Destroy())
            {
                paintView = nullptr;
            }
        }
    }
}
void laba2Frame::SwitchView(int viewId)
{
    DestroyView(currentViewId);
    CreateView(viewId);
    currentViewId = viewId;
}

void laba2Frame::OnTableDataSelected(wxCommandEvent& event)
{
    if(currentViewId == PAINT_VIEW_ID)
    {
        SwitchView(GRID_VIEW_ID);
    }
}

void laba2Frame::OnGraphicDataSelected(wxCommandEvent& event)
{
    if(currentViewId == GRID_VIEW_ID)
    {
        SwitchView(PAINT_VIEW_ID);
    }
}

void laba2Frame::OnExportMenuItem(wxCommandEvent& event)
{
    wxFileDialog fileDialog(this, _("Specify a image name and location"),
            "", "", _("*.bmp"), wxFD_SAVE);

    int response = fileDialog.ShowModal();
    if(response == wxID_OK)
    {
        wxRect rect = GetClientRect();

        wxBitmap img(rect.width, rect.height, -1);

        wxMemoryDC memoryDC;
        memoryDC.SelectObject(img);

        memoryDC.FloodFill(0, 0, *wxBLACK);

        DCUtils::draw(&memoryDC, (const wxPoints* )points, &rect);

        memoryDC.SelectObject(wxNullBitmap);
        img.SaveFile(fileDialog.GetPath(), wxBITMAP_TYPE_BMP);

        wxLogMessage(_("Image was generated"));
    }
}
